/*
 * Copyright 2007 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.gwt.sockets.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.sockets.client.impl.FlashBinarySocketImpl;
import com.google.gwt.sockets.client.impl.FlashTextSocketImpl;
import com.google.gwt.sockets.client.impl.JavaBinarySocketImpl;
import com.google.gwt.sockets.client.impl.JavaTextSocketImpl;

/**
 * <p>Creation of sockets should be done using this factory class. It determines what implementation of
 * sockets can be supported by client's browser and creates it. There are Java and Flash implementations of text
 * and binary sockets so far. For special needs it is possible to create sockets directly from implementation
 * classes.</p>
 * 
 * <p>Macromedia Flash has security restriction - it allows to connect to a server only after downloading
 * a specific file from it with security policies. This means that we cannot connect to any server, only to
 * those which have this file. But if you still want to connect to such servers you can use Java implementation
 * of sockets. See Java {@link com.google.gwt.sockets.client.impl.JavaTextSocketImpl TextSocket} and
 * {@link com.google.gwt.sockets.client.impl.JavaBinarySocketImpl BinarySocket} implementations</p>
 *
 * <p>To provide Macromedia Flash with policy file you should specify URL to that file on a server you want to
 * connect to. There are two protocols available for downloading a policy file:
 * <li>HTTP on 80 port. In this case only ports 1024 and later will be available for connecting. URL to the
 * policy file should looks like <code>http://127.0.0.1/crossdomain.xml</code>
 * <li>XMLSocket protocol. The policy file obtained by XMLSocket protocol allows to connect to any port. After
 * establishing a connection with the specified port, Flash Player transmits
 * <code>&lt;policy-file-request /&gt;</code>, terminated by a <i>null</i> byte, then server should return
 * contents of the policy file terminated by a <i>null</i> byte. URL to policy file looks like
 * <code>xmlsocket://127.0.0.1:1234</code>
 * </p>
 *
 * More information about Macromedia Flash security you can read at
 * http://livedocs.adobe.com/flash/9.0/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00005403.html
 *
 * @see com.google.gwt.sockets.client.AbstractSocket
 * @see com.google.gwt.sockets.client.TextSocket
 * @see com.google.gwt.sockets.client.BinarySocket
 * @author Aleksey Lagoshin
 */
public class SocketsFactory {

  /**
   * Returns a text socket implementation which is supported by client's browser. Also the default crossdomain
   * policy file for Flash will be downloaded from the server where this GWT application is located. The
   * default policy file allows Flash to connect to the server where this GWT application is located on
   * ports 1024 and later.
   *
   * @param listener a listener for the socket
   * @return a text socket implementation which is supported by client's browser, or null if client's browser
   * doesn't support any of existed implementations
   * @see com.google.gwt.sockets.client.AbstractSocket
   * @see com.google.gwt.sockets.client.TextSocket
   */
  public static TextSocket createTextSocket(SocketListener listener) {
    return createTextSocket(listener, getDefaultPolicyFile());
  }

  /**
   * Returns a text socket implementation which is supported by client's browser.
   *
   * @param listener a listener for the socket
   * @param policyFileURL URL to Flash security policy file location, if it is null Flash will download a
   * default crossdomain policy file from the server where this GWT application is located; the
   * default policy file allows Flash to connect to the server where this GWT application is located on
   * ports 1024 and later
   * @return a text socket implementation which is supported by client's browser, or null if client's browser
   * doesn't support any of existed implementations
   * @see com.google.gwt.sockets.client.AbstractSocket
   * @see com.google.gwt.sockets.client.TextSocket
   */
  public static TextSocket createTextSocket(SocketListener listener, String policyFileURL) {
    if (isFlashXSupported(6))
      return new FlashTextSocketImpl(listener, policyFileURL);

    if (isJavaSupported() && GWT.isScript())
      return new JavaTextSocketImpl(listener);

    return null;  
  }

  /**
   * Returns a binary socket implementation which is supported by client's browser. Also the default crossdomain
   * policy file for Flash will be downloaded from the server where this GWT application is located. The
   * default policy file allows Flash to connect to the server where this GWT application is located on
   * ports 1024 and later.
   *
   * @param listener a listener for the socket
   * @return a binary socket implementation which is supported by client's browser, or null if client's browser
   * doesn't support any of existed implementations.
   * @see com.google.gwt.sockets.client.AbstractSocket
   * @see com.google.gwt.sockets.client.BinarySocket
   */
  public static BinarySocket createBinarySocket(SocketListener listener) {
    return createBinarySocket(listener, getDefaultPolicyFile());
  }

  /**
   * Returns a binary socket implementation which is supported by client's browser.
   *
   * @param listener a listener for the socket
   * @param policyFileURL URL to Flash security policy file location, if it is null Flash will download a
   * default crossdomain policy file from the server where this GWT application is located; the
   * default policy file allows Flash to connect to the server where this GWT application is located on
   * ports 1024 and later
   * @return a binary socket implementation which is supported by client's browser, or null if client's browser
   * doesn't support any of existed implementations
   * @see com.google.gwt.sockets.client.AbstractSocket
   * @see com.google.gwt.sockets.client.BinarySocket
   */
  public static BinarySocket createBinarySocket(SocketListener listener, String policyFileURL) {
    if (isFlashXSupported(9))
      return new FlashBinarySocketImpl(listener, policyFileURL);

    if (isJavaSupported() && GWT.isScript())
      return new JavaBinarySocketImpl(listener);

    return null;
  }

  /**
   * Determines if client's browser supports Java Plugin. Java Plugin is needed for Java implementaions of Text
   * and Binary Sockets.
   *
   * @return true if Java Plugin is supported by client's browser
   */
  public static native boolean isJavaSupported()/*-{
    return (typeof $wnd.navigator.javaEnabled != undefined && $wnd.navigator.javaEnabled());
  }-*/;

  /**
   * Determines if client's browser supports Macromedia Flash. Flash 6+ is needed for Flash implementation of Text
   * sockets and Binary Sockets requires Flash 9+.
   *
   * @param version the verison of Flash
   * @return true if Flash 9 is supported by client's browser
   */
  public static native boolean isFlashXSupported(int version) /*-{
    // If browser is Internet Explorer
    if ($wnd.navigator.userAgent.indexOf("MSIE") != -1)
      try {
        return (typeof new ActiveXObject("ShockwaveFlash.ShockwaveFlash." + version) == "object");
      }
      catch (e) {
        return false;
      }
    // Other browsers
    else {
      var flashPlugin = $wnd.navigator.plugins["Shockwave Flash"];
      if (flashPlugin == undefined)
        return false;
      var flashDescription = flashPlugin.description;
      if (flashDescription == undefined)
        return false;
      return flashDescription.charAt(flashDescription.indexOf(".") - 1) >= version;
    }
  }-*/;

  /**
   * Returns URL to the default crossdomain policy file for Flash (see crossdomain.xml in public folder).
   * This policy file allows Flash to connect to server where this GWT application is located on ports
   * 1024 and later.
   *
   * @return URL to crossdomain policy file.
   */
  public static native String getDefaultPolicyFile() /*-{
    var result;
    var href = $wnd.location.href;
    var index = href.indexOf("?");

    result = index != -1 ? href.substr(0, index) : href;

    return result.substr(0, result.lastIndexOf("/") + 1) + "crossdomain.xml";
  }-*/;

}
